﻿#include "FITKGraphObjectLoad_F_M.h"

// VTK
#include <vtkArrowSource.h>
#include <vtkPoints.h>
#include <vtkVertex.h>
#include <vtkCellArray.h>
#include <vtkPolyData.h>
#include <vtkPointData.h>
#include <vtkDoubleArray.h>
#include <vtkGlyph3D.h>
#include <vtkDistanceToCamera.h>
#include <vtkActor.h>
#include <vtkProperty.h>
#include <vtkTransform.h>
#include <vtkSelectionNode.h>
#include <vtkMaskPoints.h>

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITKGraphCommons.h"

// Data
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaLoadConcentratedForce.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaLoadMoment.h"
#include "FITK_Component/FITKAbaqusData/FITKAbaModelInfo.h"
#include "FITK_Interface/FITKInterfaceModel/FITKCoordinateSystem.h"

#include "FITK_Interface/FITKInterfaceStructural/FITKAbaRefPoint.h"
#include "FITK_Kernel/FITKCore/FITKVec3D.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaAssembly.h"
#include "FITK_Interface/FITKInterfaceModel/FITKModelSet.h"

#include "FITK_Component/FITKAbaqusData/FITKAbaqusPart.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAbstractAssInstance.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaMeshPartVTK.h"

namespace Exchange
{
    FITKGraphObjectLoad_F_M::FITKGraphObjectLoad_F_M(AbaqusData::FITKDataCase* caseData, Interface::FITKAbaLoadConcentratedForce* forceData, int stepId)
        : FITKGraphObjectNodeLoadBase(caseData, forceData, stepId)
    {
        if (!forceData)
        {
            return;
        }

        // Set the load data type.
        m_details = 1;

        // The force value.
        getValue(m_stepId);

        // Generate the load's node set poly data.
        bool flag = generatePolyData(forceData);
        if (!flag)
        {
            return;
        }

        // Generate load actors.
        generateGraphs();

        // Save the graph information.
        m_graphInfo.Type = OtherGraph;

        // Clear cache.
        // clearCache();

        // Update the visibility.
        updateVisibility();
    }

    FITKGraphObjectLoad_F_M::FITKGraphObjectLoad_F_M(AbaqusData::FITKDataCase* caseData, Interface::FITKAbaLoadMoment* momentData, int stepId)
        : FITKGraphObjectNodeLoadBase(caseData, momentData, stepId)
    {
        if (!momentData)
        {
            return;
        }

        // Set the load data type.
        m_details = 2;

        // The moment value.
        getValue(m_stepId);

        // Generate the load's node set poly data.
        bool flag = generatePolyData(momentData);
        if (!flag)
        {
            return;
        }

        // Generate load actors.
        generateGraphs();

        // Save the graph information.
        m_graphInfo.Type = OtherGraph;

        // Clear cache.
        // clearCache();

        // Update the visibility.
        updateVisibility();
    }

    void FITKGraphObjectLoad_F_M::getValue(int stepId)
    {
        // Get the coodinate from the load( If exist. ).
        m_coordinate = nullptr;

        // Get force or moment by the step index.
        if (m_details == 1)
        {
            Interface::FITKAbaLoadConcentratedForce* forceData = dynamic_cast<Interface::FITKAbaLoadConcentratedForce*>(_dataObj);
            if (forceData)
            {
                m_value[0] = forceData->getCF1(stepId);
                m_value[1] = forceData->getCF2(stepId);
                m_value[2] = forceData->getCF3(stepId);

                m_coordinate = forceData->getCsysObj();
            }
        }
        else if (m_details == 2)
        {
            Interface::FITKAbaLoadMoment* momentData = dynamic_cast<Interface::FITKAbaLoadMoment*>(_dataObj);
            if (momentData)
            {
                m_value[0] = momentData->getCM1(stepId);
                m_value[1] = momentData->getCM2(stepId);
                m_value[2] = momentData->getCM3(stepId);

                m_coordinate = momentData->getCsysObj();
            }
        }

        // Get the direction from coodinate.
        if (m_coordinate)
        {
            // Save the coordinate ID.
            m_coordinateId = m_coordinate->getDataObjectID();
        }

        // Get the glyph positive or negative directions.
        m_symbolDirecs[0] = qFuzzyCompare(m_value[0], 0) ? 0 : m_value[0] / fabs(m_value[0]);
        m_symbolDirecs[1] = qFuzzyCompare(m_value[1], 0) ? 0 : m_value[1] / fabs(m_value[1]);
        m_symbolDirecs[2] = qFuzzyCompare(m_value[2], 0) ? 0 : m_value[2] / fabs(m_value[2]);
    }

    void FITKGraphObjectLoad_F_M::generateGraphs()
    {
        // Set the glyph size.
        m_glyphSize = m_defaultSize;
 
        // Create source.
        vtkArrowSource* arrowSource = vtkArrowSource::New();
        arrowSource->SetShaftRadius(0);
        arrowSource->SetTipLength(0.2);
        arrowSource->SetTipRadius(0.1);
        arrowSource->SetTipResolution(m_sourceResolution);
        m_sourceList.push_back(arrowSource);

        // Create actors. ( Point load )
        if (m_details == 1)
        {
            generateGraph(arrowSource);
        }
        else if (m_details == 2)
        {
            double scale = 0.85;

            arrowSource->SetTipLength(0.25);

            vtkArrowSource* arrowSource2 = vtkArrowSource::New();
            arrowSource2->SetShaftRadius(0);
            arrowSource2->SetTipLength(arrowSource->GetTipLength() * (1 / scale));
            arrowSource2->SetTipRadius(arrowSource->GetTipRadius() * (1 / scale));
            arrowSource2->SetTipResolution(m_sourceResolution);
            m_sourceList.push_back(arrowSource2);

            generateGraph(arrowSource);
            generateGraph(arrowSource2, m_glyphSize * scale);
        }
    }

    void FITKGraphObjectLoad_F_M::insertNextPoint(double* pos, int modelId, bool addToLoadPoints)
    {
        // Details (1: Force, 2 : Moment)
        if (!pos || !m_polyData || !m_points || !m_cells || !m_normal || !m_scalar || !m_modelIdArray)
        {
            return;
        }

        auto addPoint = [&](double* position)
        {
            int ptId = m_points->InsertNextPoint(position);

            vtkSmartPointer<vtkVertex> vertex = vtkSmartPointer<vtkVertex>::New();
            vertex->GetPointIds()->SetId(0, ptId);
            m_cells->InsertNextCell(ptId);

            if (addToLoadPoints)
            {
                m_modelIdArray->InsertNextValue(modelId);
            }
        };

        // Save the points for refreshing actors.
        if (addToLoadPoints)
        {
            m_loadPoints->InsertNextPoint(pos);
        }

        // Get the direction of the load.
        double direcX[3]{ 1., 0., 0. };
        double direcY[3]{ 0., 1., 0. };
        double direcZ[3]{ 0., 0., 1. };

        // Try to get the direction by coordinate transform.
        if (m_coordinate)
        {
            double direcXYZ[9]{ 1., 0., 0., 0., 1., 0., 0., 0., 1. };
            m_coordinate->get3DirectionAtPoint(pos, direcXYZ);

            direcX[0] = direcXYZ[0];
            direcX[1] = direcXYZ[1];
            direcX[2] = direcXYZ[2];

            direcY[0] = direcXYZ[3];
            direcY[1] = direcXYZ[4];
            direcY[2] = direcXYZ[5];

            direcZ[0] = direcXYZ[6];
            direcZ[1] = direcXYZ[7];
            direcZ[2] = direcXYZ[8];
        }

        // Add the symbol to directions.
        for (int i = 0; i < 3; i++)
        {
            direcX[i] *= m_symbolDirecs[0];
            direcY[i] *= m_symbolDirecs[1];
            direcZ[i] *= m_symbolDirecs[2];
        }

        // Add tripple points, cells, normal and scalar for x, y, z load.
        if (!qFuzzyCompare(m_value[0], 0))
        {
            addPoint(pos);
            m_normal->InsertNextTuple3(direcX[0], direcX[1], direcX[2]);
            m_scalar->InsertNextValue(1);
        }

        if (!qFuzzyCompare(m_value[1], 0))
        {
            addPoint(pos);
            m_normal->InsertNextTuple3(direcY[0], direcY[1], direcY[2]);
            m_scalar->InsertNextValue(1);
        }

        if (!qFuzzyCompare(m_value[2], 0))
        {
            addPoint(pos);
            m_normal->InsertNextTuple3(direcZ[0], direcZ[1], direcZ[2]);
            m_scalar->InsertNextValue(1);
        }
    }

    void FITKGraphObjectLoad_F_M::initActorProperties(vtkProp* actor, QVariant details)
    {
        if (!actor)
        {
            return;
        }

        // Set model actor properties.
        // Default show face and edge.
        FITKGraphActor* fActor = FITKGraphActor::SafeDownCast(actor);
        if (!fActor)
        {
            return;
        }

        // Set the scalar off.
        fActor->setScalarVisibility(false);
        fActor->GetProperty()->SetLineWidth(m_glyphLineWidth);
        fActor->GetProperty()->SetRepresentation(1);

        if (m_details == 1)
        {
            double color3[3] = { 0 ,0, 0 };
            FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_forceLoadColor, color3);
            fActor->GetProperty()->SetColor(color3);
        }
        else if (m_details == 2)
        {
            double color3[3] = { 0 ,0, 0 };
            FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_momentLoadColor, color3);
            fActor->GetProperty()->SetColor(color3);
        }
    }

    bool FITKGraphObjectLoad_F_M::checkDataChanged()
    {
        // Get the current coodiante from force or moment.
        Interface::FITKCoordinateSystem* coordinate{ nullptr };

        if (m_details == 1)
        {
            Interface::FITKAbaLoadConcentratedForce* forceData = dynamic_cast<Interface::FITKAbaLoadConcentratedForce*>(_dataObj);
            if (forceData)
            {
                coordinate = forceData->getCsysObj();
            }
        }
        else if (m_details == 2)
        {
            Interface::FITKAbaLoadMoment* momentData = dynamic_cast<Interface::FITKAbaLoadMoment*>(_dataObj);
            if (momentData)
            {
                coordinate = momentData->getCsysObj();
            }
        }

        int currentCoordId = -1;
        if (coordinate)
        {
            currentCoordId = coordinate->getDataObjectID();
        }

        // Check if the coordinate changed.
        return currentCoordId != m_coordinateId;
    }
}   // namespace Exchange